<script lang="ts" setup>
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
import type { Key } from 'ant-design-vue/es/vc-tree/interface';
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';

import type { Nullable } from '@vben/types';

import type { DefApplicationModel, DefResourceModel } from '#/api';
import type { TreeActionType } from '#/components/tree';

import { onMounted, reactive, ref, unref } from 'vue';

import { Checkbox, Tag } from 'ant-design-vue';
import { difference, intersection, uniq } from 'lodash-es';

import { CollapseContainer } from '#/components/container';
import { Icon } from '#/components/icon';
import { BasicTree } from '#/components/tree';
import { getResourceTagColor } from '#/utils/color';
import {
  eachTree,
  findChildrenByParentId,
  getById,
} from '#/utils/helper/treeHelper';

defineOptions({
  name: 'ApplicationTree',
  inheritAttrs: false,
});

const props = withDefaults(defineProps<Props>(), {
  application: () => ({}) as DefApplicationModel.DefApplicationResultVO,
  resourceList: () => [],
});

interface Props {
  application?: DefApplicationModel.DefApplicationResultVO;
  resourceList?: DefResourceModel.DefResourceResultVO[];
}

const treeRef = ref<Nullable<TreeActionType>>(null);
// 临时数据
const state = reactive({
  indeterminate: false,
  checkAll: false,
  allKeys: [] as string[],
});

function getTree() {
  const tree = unref(treeRef);
  return tree;
}

onMounted(async () => {
  const keys = [] as string[];

  eachTree(
    props.resourceList,
    (item, parent) => {
      keys.push(item.id);
      item.key = item.id;
      item.title = item.name;
      item.keyLinks = [...(parent.keyLinks || []), item.id];
      item.slots = { title: 'title' };
      return item;
    },
    {},
  );

  state.allKeys = keys;
});

// 获取 树当前选中的节点key
function getCheckedKeys(): string[] {
  const checkedKeys = getTree()?.getCheckedKeys();
  const checked = getTree()?.getCheckStrictly()
    ? (checkedKeys as any).checked
    : checkedKeys;
  return checked || [];
}

function setCheckedKeys(keys: number[] | string[]) {
  const checked = getTree()?.getCheckStrictly()
    ? {
        checked: keys,
        halfChecked: [],
      }
    : keys;
  getTree()?.setCheckedKeys(checked);
}

// 计算 半选状态
async function computedIndeterminate() {
  const checkedKeys = getCheckedKeys();
  state.indeterminate =
    checkedKeys.length > 0 && checkedKeys.length < state.allKeys.length;
}

// 计算 选中状态
async function computedCheckAll() {
  const checkedKeys = getCheckedKeys();
  state.checkAll =
    state.allKeys.length > 0 && state.allKeys.length === checkedKeys.length;
}

/**
 * 全选应用
 *
 * @param checked 是否选中
 */
function onCheckAllChange(e: CheckboxChangeEvent) {
  e?.stopPropagation();
  e?.preventDefault();
  getTree()?.checkAll(e.target.checked);

  computedIndeterminate();
}

/**
 * 选中tree的复选框
 *
 * 1. 选中某个节点: 将其所有的父节点都选中
 * 2. 取消某个节点: 将其所有子节点全部取消
 *
 * @param checkedKeys 已选中的key
 * @param checked 选中还是取消
 * @param node 当前节点
 */
async function checkNode(_: any, { checked, node }: CheckInfo) {
  if (checked) {
    // 选中

    // 查找当前节点
    const current = getById(node?.eventKey, props.resourceList);
    // 查找当前节点的所有父Id

    const checkedKeys = getCheckedKeys();
    // 同时勾选上所有的父节点
    const newKeys = [...checkedKeys, ...(current?.keyLinks as string[])];
    setCheckedKeys(uniq(newKeys));
  } else {
    // 取消选中

    // 查找当前节点的所有子节点
    const childrenIds = findChildrenByParentId(
      node?.eventKey as Key,
      props.resourceList,
    );
    const checkedKeys = getCheckedKeys();
    // 设置新的选中节点为： 当前已经选中节点 - 当前节点的子节点
    const newKeys = difference(checkedKeys, childrenIds);
    setCheckedKeys(uniq(newKeys) as string[]);
  }

  computedCheckAll();
  computedIndeterminate();
}

/**
 * 全选或取消全选
 *
 * 全选逻辑：将所有子节点和父节点选中
 *
 * 取消全选逻辑：将所有子节点取消
 *
 * @param id 节点id
 * @param e 事件
 */
async function selectAll(id: Key, e: any) {
  e?.stopPropagation();
  e?.preventDefault();

  // 查找当前节点的所有子节点id
  const childrenIds = findChildrenByParentId(id, props.resourceList);
  const checkedKeys = getCheckedKeys();
  if (await containsAll(childrenIds)) {
    // 取消全选
    // 将已经选中的节点 - 当前节点的所有子节点
    const newKeys = difference(checkedKeys, childrenIds);
    setCheckedKeys(newKeys as string[]);
  } else {
    // 全选

    const newKeys = [...checkedKeys, ...childrenIds];

    // 查找当前节点
    const current = getById(id, props.resourceList);
    // 查找当前节点的所有父Id
    const parentAndChildrenIds = [...newKeys, ...(current?.keyLinks as Key[])];

    setCheckedKeys(uniq(parentAndChildrenIds) as string[]);
  }

  computedCheckAll();
  computedIndeterminate();
}

/**
 * 某个节点是 全选 还是 全部取消
 *
 * @param id
 */
function isAllCheckedByKey(id: Key): boolean {
  const childrenIds = findChildrenByParentId(id, props.resourceList);
  return containsAll(childrenIds);
}

/**
 * 指定的 节点id 是否全部被选中
 *
 * @param ids
 */
function containsAll(ids: Key[]): boolean {
  if (!ids || ids.length === 0) {
    return false;
  }
  const checkedKeys = getCheckedKeys();
  return intersection(checkedKeys, ids).length === ids.length;
}

defineExpose({ treeRef, state, selectAll });
</script>

<template>
  <CollapseContainer class="appResource mb-4">
    <template #title>
      <Checkbox
        :value="application.id"
        @change="onCheckAllChange"
        :indeterminate="state.indeterminate"
        v-model:checked="state.checkAll"
      >
        <Tag color="error">应用</Tag>
        {{ application.name }}
      </Checkbox>
    </template>
    <BasicTree
      checkable
      check-strictly
      :click-row-to-expand="false"
      :tree-data="resourceList"
      ref="treeRef"
      @check="checkNode"
      toolbar
      :toolbar-strictly="false"
      search
      highlight
    >
      <template #title="item">
        <Icon class="ml-2" :icon="item.icon" v-if="item.icon" />
        <template v-if="item.echoMap?.resourceType">
          <Tag
            class="ml-2"
            :color="getResourceTagColor(item?.resourceType, item.isHidden)"
          >
            {{ item.echoMap?.resourceType }}
          </Tag>
        </template>
        {{ item.name }}
        <span>
          <a
            v-if="item.children && item.children.length > 0"
            @click="selectAll(item.id, $event)"
            class="ml-5"
          >
            {{ isAllCheckedByKey(item.id) ? '取消全选' : '全选' }}
          </a>
        </span>
      </template>
    </BasicTree>
  </CollapseContainer>
</template>

<style lang="scss" scoped>
.appResource {
  border: 1px solid #d9d9d9;
}
</style>
